home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-13
/
xvisrc.zip
/
MOVEMENT.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-07-28
|
13KB
|
615 lines
/* Copyright (c) 1990,1991,1992 Chris and John Downey */
#ifndef lint
static char *sccsid = "@(#)movement.c 2.2 (Chris & John Downey) 9/1/92";
#endif
/***
* program name:
xvi
* function:
PD version of UNIX "vi" editor, with extensions.
* module name:
movement.c
* module function:
Movement of the cursor and the window into the buffer.
* history:
STEVIE - ST Editor for VI Enthusiasts, Version 3.10
Originally by Tim Thompson (twitch!tjt)
Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
Heavily modified by Chris & John Downey
***/
#include "xvi.h"
/*
* Shift a buffer's contents down relative to its window,
* but don't change the display.
*
* Return number of physical lines shifted.
*/
int
shiftdown(win, nlines)
register Xviwin *win;
unsigned nlines;
{
register unsigned k; /* loop counter */
int done = 0; /* # of physical lines done */
for (k = 0; k < nlines; k++) {
register Line *p;
register long physlines;
/*
* Set the top screen line to the previous one.
*/
p = win->w_topline->l_prev;
if (p == win->w_buffer->b_line0)
break;
physlines = plines(win, p);
done += LONG2INT(physlines);
win->w_topline = p;
}
win->w_curs_new = TRUE;
return(done);
}
/*
* Shift a buffer's contents up relative to its window,
* but don't change the display.
*
* Return number of physical lines shifted.
*/
int
shiftup(win, nlines)
register Xviwin *win;
unsigned nlines;
{
register unsigned k; /* loop counter */
int done = 0; /* # of physical lines done */
for (k = 0; k < nlines; k++) {
register Line *p;
register long physlines;
/*
* Set the top screen line to the next one.
*/
p = win->w_topline->l_next;
if (p == win->w_buffer->b_lastline)
break;
physlines = plines(win, win->w_topline);
done += LONG2INT(physlines);
win->w_topline = p;
}
win->w_curs_new = TRUE;
return(done);
}
/*
* Scroll the screen down 'nlines' lines.
*/
void
scrolldown(win, nlines)
register Xviwin *win;
unsigned nlines;
{
s_ins(win, 0, shiftdown(win, nlines));
}
/*
* Scroll the screen up 'nlines' lines.
*/
void
scrollup(win, nlines)
register Xviwin *win;
unsigned nlines;
{
s_del(win, 0, shiftup(win, nlines));
}
/*
* oneup
* onedown
* one_left
* one_right
*
* Move cursor one char {left,right} or one line {up,down}.
*
* Return TRUE when successful, FALSE when we hit a boundary
* (of a line, or the file).
*/
/*
* Move the cursor up 'nlines' lines.
*
* Returns TRUE if we moved at all.
*/
bool_t
oneup(win, nlines)
register Xviwin *win;
long nlines;
{
Posn *pp;
register Line *curr_line;
long k;
pp = win->w_cursor;
curr_line = pp->p_line;
for (k = 0; k < nlines; k++) {
/*
* Look for the previous line.
*/
if (curr_line == win->w_buffer->b_file) {
/*
* If we've at least backed up a little ...
*/
if (k > 0) {
break; /* to update the cursor, etc. */
} else {
return(FALSE);
}
}
curr_line = curr_line->l_prev;
}
pp->p_line = curr_line;
pp->p_index = 0;
win->w_curs_new = TRUE;
/*
* Try to advance to the column we want to be at.
*/
coladvance(win, win->w_curswant);
return(TRUE);
}
/*
* Move the cursor down 'nlines' lines.
*
* Returns TRUE if we moved at all.
*/
bool_t
onedown(win, nlines)
register Xviwin *win;
long nlines;
{
Posn *pp;
register Line *curr_line;
long k;
pp = win->w_cursor;
curr_line = pp->p_line;
for (k = 0; k < nlines; k++) {
/*
* Look for the next line.
*/
if (curr_line->l_next == win->w_buffer->b_lastline) {
if (k > 0) {
break;
} else {
return(FALSE);
}
}
curr_line = curr_line->l_next;
}
pp->p_line = curr_line;
pp->p_index = 0;
win->w_curs_new = TRUE;
/*
* Try to advance to the column we want to be at.
*/
coladvance(win, win->w_curswant);
return(TRUE);
}
/*ARGSUSED*/
bool_t
one_left(window, unused)
Xviwin *window;
bool_t unused;
{
Posn *p;
window->w_set_want_col = TRUE;
p = window->w_cursor;
if (p->p_index > 0) {
p->p_index--;
curs_horiz(window, -1);
return(TRUE);
} else {
return(FALSE);
}
}
/*
* The move_past_end parameter will be TRUE if moving past the end
* of the line (onto the first '\0' character) is allowed. We will
* never move past this character.
*/
bool_t
one_right(window, move_past_end)
Xviwin *window;
bool_t move_past_end;
{
Posn *p;
char *txtp;
window->w_set_want_col = TRUE;
p = window->w_cursor;
txtp = &p->p_line->l_text[p->p_index];
if (txtp[0] != '\0' && (move_past_end || txtp[1] != '\0')) {
p->p_index++;
curs_horiz(window, 1);
return(TRUE);
} else {
return(FALSE);
}
}
void
begin_line(window, flag)
Xviwin *window;
bool_t flag;
{
register Posn *pos;
register int c;
pos = window->w_cursor;
if (flag) {
char *startp;
register char *p;
startp = p = pos->p_line->l_text;
while ((c = *p) != '\0' && p[1] != '\0' && is_space(c)) {
p++;
}
pos->p_index = p - startp;
} else {
pos->p_index = 0;
}
window->w_set_want_col = TRUE;
window->w_curs_new = TRUE;
}
/*
* coladvance(win, col)
*
* Try to advance to the specified column, starting at p.
*/
void
coladvance(win, col)
register Xviwin *win;
register int col;
{
register int c;
register char *tstart, *tp;
tp = tstart = win->w_cursor->p_line->l_text;
/*
* Try to advance to the specified column.
*/
for (c = 0; c < col; ) {
if (*tp == '\0') {
/*
* We're at the end of the line.
*/
break;
}
c += vischar(*tp, (char **) NULL, (Pb(P_list) ? -1 : c));
tp++;
}
/*
* Move back onto last character if we have to.
*/
if ((*tp == '\0' || c > col) && tp > tstart)
--tp;
win->w_cursor->p_index = tp - tstart;
curwin->w_curs_new = TRUE;
}
/*
* Go to the specified line number in the current buffer.
* Position the cursor at the first non-white.
*/
void
do_goto(line)
long line;
{
curwin->w_cursor->p_line = gotoline(curbuf, line);
curwin->w_cursor->p_index = 0;
curwin->w_curs_new = TRUE;
begin_line(curwin, TRUE);
}
/*
* Move the cursor to the specified line, at the specified position.
*/
void
move_cursor(win, lp, index)
Xviwin *win;
Line *lp;
int index;
{
win->w_cursor->p_line = lp;
win->w_cursor->p_index = index;
win->w_curs_new = TRUE;
}
/*
* Adjust window so that currline is, as far as possible, in the
* middle of it.
*
* Don't update the screen: move_window_to_cursor() does that.
*/
static void
jump(win, currline, halfwinsize)
Xviwin *win;
Line *currline;
int halfwinsize;
{
register int count;
register int spare;
register Line *topline;
register Line *filestart = win->w_buffer->b_file;
spare = win->w_nrows - (unsigned int) plines(win, topline = currline) - 1;
for (count = 0; count < halfwinsize && topline != filestart;) {
topline = topline->l_prev;
count += (unsigned int) plines(win, topline);
if (count >= spare) {
if (count > spare)
topline = topline->l_next;
break;
}
}
win->w_topline = topline;
update_buffer(win->w_buffer);
/*
* The result of calling show_file_info here is that if the
* cursor moves a long away - e.g. for a "G" command or a search
* - the status line is updated with the correct line number.
* This is a small cost compared to updating the whole window.
*/
show_file_info(win);
}
/*
* Update the position of the window relative to the buffer, moving
* the window if necessary to ensure that the cursor is inside the
* window boundary. w_topline, w_botline and w_cursor must be set to
* something reasonable for us to be able to test whether the cursor
* is in the window or not, unless (as a special case) the buffer is
* empty - in this case, the cursor sits at top left, despite there
* being no character there for it to sit on.
*
* If we have to move the window only a small amount, we try to scroll
* it rather than redrawing. If we have to redraw, we also rewrite the
* status line on the principle that it's probably a negligible cost
* compared to updating the whole window.
*/
void
move_window_to_cursor(win)
register Xviwin *win;
{
register Line *currline;
int halfwinsize;
long distance;
currline = win->w_cursor->p_line;
halfwinsize = (win->w_nrows - 1) / 2;
/*
* First stage: move window towards cursor.
*/
if (bufempty(win->w_buffer)) {
/*
* Special case - file is empty.
*/
win->w_topline = win->w_buffer->b_file;
win->w_cursor->p_line = win->w_buffer->b_file;
win->w_cursor->p_index = 0;
win->w_curs_new = TRUE;
} else if (earlier(currline, win->w_topline)) {
long nlines;
/*
* The cursor is above the top of the window; move the
* window towards it.
*/
nlines = cntplines(win, currline, win->w_topline);
/*
* Decide whether it's worthwhile - & possible - to
* scroll to get the window to the right place.
*
* It's possible if we can have scrolling regions, or
* we can insert screen lines & the window is at the
* bottom of the screen.
*
* The actual scrolling is done by s_ins(), in
* screen.c.
*
* If Pn(P_jumpscroll) is js_OFF, we don't use
* jumpscroll anyway.
*/
if (
nlines > halfwinsize
||
Pn(P_jumpscroll) == js_ON
||
(
!can_scroll_area
&&
Pn(P_jumpscroll) == js_AUTO
&&
(
!can_ins_line
||
win->w_cmdline < (Rows - 1)
)
)
) {
jump(win, currline, halfwinsize);
} else {
s_ins(win, 0, (int) nlines);
win->w_topline = currline;
update_window(win);
}
} else {
long nlines;
/*
* The cursor is on or after the last line of the screen,
* so we might have to move the screen to it. Check to see
* whether we are actually off the screen; if not, we don't
* have to do anything. We do this in a certain way so as
* not to do any unnecessary calculations; this routine is
* called very often, so we must not take too much time.
*/
if (earlier(currline, win->w_botline->l_prev) ||
(plines(win, currline) == 1 &&
earlier(currline, win->w_botline))) {
return;
}
distance = cntplines(win, win->w_topline, currline->l_next);
if (distance <= win->w_nrows - 1) {
return;
}
/*
* The cursor is off the bottom of the window, or the
* line the cursor is on won't completely fit in the
* window.
*/
nlines = distance - (win->w_nrows - 1);
/*
* Decide whether it's worthwhile - & possible - to
* scroll to get the window to the right place.
*
* It's possible if we can have scrolling regions, or
* we can delete screen lines & the window is at the
* bottom of the screen, or it's the only window on
* the screen.
*
* The actual scrolling is done by s_del(), in
* screen.c.
*
* If Pn(P_jumpscroll) is js_OFF, we don't use
* jumpscroll anyway.
*/
if (
nlines > halfwinsize
||
Pn(P_jumpscroll) == js_ON
||
(
!can_scroll_area
&&
Pn(P_jumpscroll) == js_AUTO
&&
(
(
!can_del_line
&&
win->w_winpos != 0
)
||
win->w_cmdline < (Rows - 1)
)
)
) {
jump(win, currline, halfwinsize);
} else {
long done = 0;
Line *l;
Line *newtopline;
/*
* Work out where we should place topline in
* order that the cursor line is made the
* bottom line of the screen.
*/
for (l = currline;; l = l->l_prev) {
done += plines(win, l);
if (done >= win->w_nrows - 1)
break;
if (l == win->w_buffer->b_file)
break;
}
while (done > win->w_nrows - 1 && l != currline) {
done -= plines(win, l);
l = l->l_next;
}
newtopline = l;
/*
* Now work out how many screen lines we want
* to scroll the window by. This code assumes
* that the old value of topline is earlier
* in the buffer than the new, so check this
* first.
*/
if (earlier(win->w_topline, newtopline)) {
done = cntplines(win, win->w_topline, newtopline);
if (done != 0) {
s_del(win, 0, (int) done);
}
}
win->w_topline = newtopline;
update_window(win);
}
}
}
/*
* Make sure the cursor is within the given window.
*
* This is needed for commands like control-E & control-Y.
*/
void
move_cursor_to_window(win)
Xviwin *win;
{
Posn *cp;
cp = win->w_cursor;
if (earlier(cp->p_line, win->w_topline)) {
cp->p_line = win->w_topline;
coladvance(win, win->w_curswant);
} else if (!earlier(cp->p_line, win->w_botline)
&& earlier(win->w_topline, win->w_botline)) {
cp->p_line = win->w_botline->l_prev;
coladvance(win, win->w_curswant);
}
win->w_curs_new = TRUE;
}